#定义数据库的类
from app import db,app,login_manager
from datetime import datetime

from werkzeug.security import generate_password_hash,check_password_hash
from flask_login import UserMixin
from hashlib import md5

#令牌
import jwt
from time import time



#关系的关联表
followers = db.Table(
    'followers',
    db.Column('follower_id',db.Integer,db.ForeignKey("user.id")),
    db.Column('followed_id',db.Integer,db.ForeignKey("user.id"))
)


class User(UserMixin,db.Model):
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(64),index=True,unique=True)
    email = db.Column(db.String(120),index=True,unique=True)
    password_hash = db.Column(db.String(128))
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime,default=datetime.utcnow)

    #db.relationship的第一个参数表示代表关系“多”的类。
    #  backref参数定义了代表“多”的类的实例反向调用“一”的时候的属性名称。
    # 这将会为用户动态添加一个属性post.author，调用它将返回给该用户动态的用户实例
    posts = db.relationship('Post',backref='author',lazy='dynamic')


    #多对多的关系
    # followed = db.relationship(
    #     'User',secondary=followers,
    #     primaryjoin=(followers.c.follower_id == id),
    #     secondaryjoin=(followers.c.followed_id == id),
    #     backref=db.backref('follwers',lazy='dynamic'),
    #     lazy="dynamic")
    ##经常性碰到的问题是字段写错
    followed = db.relationship(
        'User', secondary=followers,
        primaryjoin=(followers.c.follower_id == id),
        secondaryjoin=(followers.c.followed_id == id),
        backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')

    def __repr__(self):
        return '<User {}>'.format(self.username)

    # 定义密码哈希值和验证密码(在无需持久化存储的情况下进行安全的密码验证)
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)
    #头像地址
    def avatar(self,size):
        digest = "https://www.gravatar.com/userimage/144510261/e9c6958a04ac0f65dba5b1a3fe095a14?size=120"
        return digest

        #返回随机的头像图片
        #md5(self.email.lower().encode('utf8')).hexdigest()
        #return 'https://www.gravatar.com/avatar/{}?=d=identicon&s={}'.format(digest,size)

    #关注和取消关注
    def follow(self,user):
        if not self.is_following(user):
            self.followed.append(user)
    def unfollow(self,user):
        if self.is_following(user):
            self.followed.remove(user)
    def is_following(self,user):
        return self.followed.filter(followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        followed = Post.query.join(
            followers,
            (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id)
            #.order_by(Post.timestamp.desc())
        own = Post.query.filter_by(user_id=self.id)
        return followed.union(own).order_by(Post.timestamp.desc())

    #令牌生成和验证
    def get_reset_password_token(self,expire_in=600):
        return jwt.encode(
            {"rest_password":self.id,'exp':time()+expire_in},
            app.config["SECRET_KEY"],algorithm='HS256').decode('utf8')

    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(
                token,app.config["SECRET_KEY"],
                algorithms=["HS256"])['rest_password']
        except:
            return
        return User.query.get(id)



@login_manager.user_loader
def load_user(id):
    return User.query.get(int(id))


#用户发表的状态，timestamp 字段将被编入索引（可以按时间顺序进行检索用户状态）
class Post(db.Model):
    id = db.Column(db.Integer,primary_key=True)
    body = db.Column(db.String(140))
    #在utcnow之后我没有包含()，所以我传递函数本身，而不是调用它的结果 UTC日期格式
    timestamp = db.Column(db.DateTime,index=True,default=datetime.utcnow)
    #user_id字段被初始化为user.id的外键，这意味着它引用了来自用户表的id值。本处的user是数据库表的名称
    # ，Flask-SQLAlchemy自动设置类名为小写来作为对应表的名称
    user_id = db.Column(db.Integer,db.ForeignKey('user.id'))

    def __repr__(self):
        return "<Post {}>".format(self.body)



"""
在使用数据库之前一定要先创建db.create_all()

def init_db():
    db.create_all()

    # Create a test user
    new_user = User(username='sunbing', email='297148227@qq.com')
    #new_user.display_name = ''
    db.session.add(new_user)
    db.session.commit()
"""